from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.actions.action_builder import ActionBuilder
from selenium.webdriver.common.keys import Keys
import time
import base64
import re
import os
import uuid
import json
from openai import OpenAI
from PIL import Image


def AI_点击(driver, bbox):
    """
    根据坐标区域点击元素
    driver: WebDriver实例
    bbox: 元素的边界框 [x1, y1, x2, y2]
    """
    print("元素坐标信息bbox", bbox)
    # 根据 bbox 计算中心点
    x_coordinate = (bbox[0] + bbox[2]) / 2
    y_coordinate = (bbox[1] + bbox[3]) / 2

    print(f"元素中心点坐标信息: {x_coordinate}, {y_coordinate}")
    # 使用ActionBuilder移动到指定位置并点击
    action_builder = ActionBuilder(driver)
    action_builder.pointer_action.move_to_location(x_coordinate, y_coordinate).click()
    action_builder.perform()
    time.sleep(1)  # 等待点击生效


def AI_输入(driver, bbox, text):
    """
    AI视觉输入函数
    driver: WebDriver实例
    bbox: 元素的边界框 [x1, y1, x2, y2]
    text: 要输入的文本
    """
    print(f"开始输入操作，目标文本: '{text}'，元素区域: {bbox}")
    
    # 确保bbox是数字类型
    bbox = [float(coord) for coord in bbox]
    
    # 计算元素中心点
    center_x = (bbox[0] + bbox[2]) / 2
    center_y = (bbox[1] + bbox[3]) / 2
    
    # 导入ActionChains用于更可靠的交互
    from selenium.webdriver.common.action_chains import ActionChains
    
    # 使用ActionBuilder进行点击和输入（完全基于AI视觉）
    success = False
    try:
        print(f"执行策略：使用ActionBuilder基于AI视觉坐标点击并输入")
        
        # 先使用ActionBuilder点击元素
        action_builder = ActionBuilder(driver)
        action_builder.pointer_action.move_to_location(center_x, center_y).click()
        action_builder.perform()
        time.sleep(0.5)
        
        # 然后使用ActionChains输入文本（因为ActionBuilder没有send_keys方法）
        actions = ActionChains(driver)
        actions.send_keys(text)
        actions.perform()
        time.sleep(1)
        
        # 验证是否成功
        active_ele = driver.switch_to.active_element
        if active_ele.tag_name in ['input', 'textarea']:
            value = active_ele.get_attribute('value')
            if value and text in value:
                print(f"✅ AI视觉输入成功！文本 '{text}' 已正确输入")
                success = True
    except Exception as e:
        print(f"AI视觉输入失败: {e}")
    
    # 最后的验证
    if success:
        print("输入操作成功完成")
    else:
        print("❌ 所有输入策略均失败，请检查页面结构或元素定位是否正确")
    
    # 保存当前截图以便调试
    try:
        screenshot_path = f"input_result_{text[:6]}.png"
        driver.save_screenshot(screenshot_path)
        print(f"已保存输入后的截图到: {screenshot_path}")
    except Exception as e:
        print(f"保存截图失败: {e}")
    
    return success


def AI_文本提取(text):
    """
    提取界面文本并保存
    text: 提取的文本内容
    """
    print(f"提取到的文本内容: {text}")
    # 这里可以根据需要扩展存储逻辑
    return text


def AI操作(driver, **kwargs):
    """
    AI视觉操作主函数
    driver: WebDriver实例
    **kwargs: 操作参数，包含操作描述等
    """
    # 初始化OpenAI客户端
    ai_client = OpenAI(
        api_key="sk-531943d051864948bce3b7c335ed7c87",
        base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
    )
    
    # 提示词模板 - 优化版本，更明确地指导AI返回正确格式
    prompt = """
    请仔细分析图片，根据以下操作描述执行相应任务：
    {user_text}
    
    请严格按照以下格式返回JSON结果，不要包含任何其他解释或说明：
    ```json
    {{
        "bbox": [x1, y1, x2, y2],
        "action": "点击" 或 "输入",
        "text": "如果是输入操作，请提供要输入的文本"
    }}
    ```
    
    特别说明：
    - 如果操作描述中包含"输入"，请将action设置为"输入"，并在text字段中提供要输入的具体内容
    - 如果只是点击操作，请将action设置为"点击"
    - bbox为目标元素的左上角和右下角坐标
    """
    
    # 获取界面截图（base64格式）
    image_base64 = driver.get_screenshot_as_base64()
    
    # 生成提示词
    ai_prompt = prompt.format(user_text=kwargs["操作描述"])
    
    # 保存截图到临时文件以获取尺寸
    temp_image_path = os.path.join(os.path.dirname(__file__), f"temp_{uuid.uuid4().hex}.png")
    with open(temp_image_path, "wb") as f:
        f.write(base64.b64decode(image_base64))
    
    # 获取图片尺寸
    width, height = Image.open(temp_image_path).size
    print(f"截图尺寸：{width}, {height}")
    
    # 删除临时文件
    os.remove(temp_image_path)
    
    print(f'提示词内容：{ai_prompt}')
    
    # 调用AI模型
    try:
        completion = ai_client.chat.completions.create(
            model="qwen-vl-plus",
            messages=[{"role": "user", "content": [
                {"type": "image_url",
                 "image_url": {"url": f"data:image/png;base64,{image_base64}"}},
                {"type": "text", "text": ai_prompt}
            ]}]
        )
        
        # 处理AI返回结果
        ai_response = completion.choices[0].message.content
        print("AI执行结果：", ai_response)
        
        # 尝试提取JSON内容
        pattern = r'```json\n(.*?)```'
        match = re.search(pattern, ai_response, re.DOTALL)
        
        if match:
            json_content = match.group(1)
            result = json.loads(json_content)
            print("提取出来要处理的操作：", result)
            
            # 确保bbox是数字类型
            if 'bbox' in result:
                result['bbox'] = [float(coord) for coord in result['bbox']]

            # 坐标校准：确保坐标在有效范围内
            bbox = result['bbox']
            # 限制坐标在屏幕范围内
            result['bbox'] = [
                max(0, min(width, bbox[0])),
                max(0, min(height, bbox[1])),
                max(0, min(width, bbox[2])),
                max(0, min(height, bbox[3]))
            ]
            
            # 执行相应操作
            if result.get('action') == '点击':
                AI_点击(driver, result['bbox'])
                # 保存截图
                driver.save_screenshot(f"click_result_{uuid.uuid4().hex[:8]}.png")
            elif result.get('action') == '输入':
                # 如果AI没有返回text，尝试从操作描述中提取
                text = result.get('text', '')
                if not text and "输入" in kwargs["操作描述"]:
                    text_match = re.search(r"输入['\"](.*?)['\"]", kwargs["操作描述"])
                    if text_match:
                        text = text_match.group(1)
                AI_输入(driver, result['bbox'], text)
                # 保存截图
                driver.save_screenshot(f"input_result_{uuid.uuid4().hex[:8]}.png")
            else:
                # 默认执行点击操作
                if 'bbox' in result:
                    AI_点击(driver, result['bbox'])
                else:
                    # 如果没有bbox，尝试使用原有的坐标解析方式
                    coords = parse_coordinates(ai_response)
                    bbox = [float(coords[0] - 50), float(coords[1] - 25), 
                           float(coords[0] + 50), float(coords[1] + 25)]
                    AI_点击(driver, bbox)
            
            return result
        else:
            # 备选方案：直接解析坐标并根据操作描述决定执行点击或输入
            print("未找到JSON格式，尝试解析坐标...")
            coords = parse_coordinates(ai_response)
            # 转换为bbox格式
            bbox = [float(coords[0] - 50), float(coords[1] - 25), 
                   float(coords[0] + 50), float(coords[1] + 25)]
            
            # 根据操作描述决定执行点击或输入
            if "输入" in kwargs["操作描述"]:
                # 尝试从操作描述中提取文本
                text_match = re.search(r"输入['\"](.*?)['\"]", kwargs["操作描述"])
                text = text_match.group(1) if text_match else ""
                AI_输入(driver, bbox, text)
            else:
                AI_点击(driver, bbox)
            
            return {"bbox": bbox, "action": "点击"}
            
    except Exception as e:
        print(f"AI操作执行失败: {e}")
        # 出错时保存错误截图
        error_screenshot = f"error_ai_operation_{uuid.uuid4().hex[:8]}.png"
        driver.save_screenshot(error_screenshot)
        print(f"已保存错误截图到: {error_screenshot}")
        raise

def main():
    # 设置Chrome选项，使用最佳实践限制内容区域
    chrome_options = Options()
    # 使用标准浏览器配置，避免兼容性问题
    chrome_options.add_argument("--window-position=0,0")
    chrome_options.add_argument("--window-size=1280,800")
    # 确保渲染一致性
    chrome_options.add_argument("--disable-blink-features=AutomationControlled")
    chrome_options.add_argument("--disable-dev-shm-usage")
    chrome_options.add_argument("--no-sandbox")
    # 基本缩放设置，移除可能导致兼容性问题的高级参数
    chrome_options.add_argument("--force-device-scale-factor=1")
    # 启用最大化窗口，确保元素完全可见
    chrome_options.add_argument("--start-maximized")
    # 禁用硬件加速，减少渲染差异
    chrome_options.add_argument("--disable-gpu")
    # 禁用扩展，避免干扰
    chrome_options.add_argument("--disable-extensions")
    
    # 初始化WebDriver
    driver = webdriver.Chrome(options=chrome_options)
    
    try:
        # 打开登录页面
        driver.get("http://novel.hctestedu.com/user/login.html")
        
        # 等待页面加载
        time.sleep(3)
        
        # 使用AI_操作函数进行账号输入 - 使用更明确的提示词
        print("正在使用AI操作输入账号...")
        AI操作(driver, 操作描述="请识别账号输入框，将action设为'输入'，text设为'18522223333'")
        time.sleep(2)
        
        # # 使用AI_操作函数进行密码输入 - 使用更明确的提示词
        # print("正在使用AI操作输入密码...")
        # AI操作(driver, 操作描述="请识别密码输入框，将action设为'输入'，text设为'123456'")
        # time.sleep(2)
        
        # # 使用AI_操作函数点击登录按钮
        # print("正在使用AI操作点击登录按钮...")
        # AI操作(driver, 操作描述="请找到并点击登录按钮")
        
        # # 等待页面跳转
        # time.sleep(5)
        
        # # 截图保存登录后的页面
        # result_screenshot_path = "login_result.png"
        # driver.save_screenshot(result_screenshot_path)
        # print(f"已保存登录结果截图到: {result_screenshot_path}")
        
        # print("登录流程完成")
        
    except Exception as e:
        print(f"发生错误: {e}")
        # 出错时也保存截图以便调试
        error_screenshot_path = "error_screenshot.png"
        driver.save_screenshot(error_screenshot_path)
        print(f"已保存错误截图到: {error_screenshot_path}")
    finally:
        # 关闭浏览器
        driver.quit()
        print("浏览器已关闭")
# 仅保留坐标解析函数以支持备选方案
def parse_coordinates(ai_response):
    """
    从AI回复中解析坐标（备选方案使用）
    尝试多种格式的坐标提取
    """
    print(f"AI回复: {ai_response}")
    
    # 尝试提取类似 "(x, y)" 格式的坐标
    coord_pattern = r'\((\d+),\s*(\d+)\)'
    match = re.search(coord_pattern, ai_response)
    if match:
        x = int(match.group(1))
        y = int(match.group(2))
        return (x, y)
    
    # 尝试提取类似 "x=123, y=456" 格式的坐标
    x_pattern = r'x=([\d.]+)'
    y_pattern = r'y=([\d.]+)'
    x_match = re.search(x_pattern, ai_response)
    y_match = re.search(y_pattern, ai_response)
    if x_match and y_match:
        x = int(float(x_match.group(1)))
        y = int(float(y_match.group(1)))
        return (x, y)
    
    # 尝试提取类似 "123, 456" 格式的坐标
    numbers_pattern = r'(\d+),\s*(\d+)'
    match = re.search(numbers_pattern, ai_response)
    if match:
        x = int(match.group(1))
        y = int(match.group(2))
        return (x, y)
    
    # 如果都没找到，返回默认坐标（页面中心）
    print("无法从AI回复中解析坐标，使用默认值")
    return (960, 540)


if __name__ == "__main__":
    main()